//
// winter - Direct2D GUI framework.
// Copyright (C) 2009 Aleksandar Dezelin 
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef SYNCHRONIZATION_HOST_H
#define SYNCHRONIZATION_HOST_H

#pragma once
#include "noncopyable.h"
#include "winter_assert.h"
#include <Windows.h>

namespace winter {

namespace platform {

class critical_section_host : public services::noncopyable {
public:
  critical_section_host() {
    ::InitializeCriticalSection(&_lock);
  }

  ~critical_section_host() {
    ::DeleteCriticalSection(&_lock);
  }

  void lock() {
    ::EnterCriticalSection(&_lock);
  }

  bool try_lock() {
    return (::TryEnterCriticalSection(&_lock) == TRUE);
  }

  void unlock() {
    ::LeaveCriticalSection(&_lock);
  }

private:
  CRITICAL_SECTION _lock;
};

class mutex_host : public services::noncopyable {
public:
  mutex_host() {
    _lock = ::CreateMutex(NULL, TRUE, NULL);
    WINTER_ASSERT(_lock);
  }

  ~mutex_host() {
    ::CloseHandle(_lock);
  }

  void lock() {
    DWORD error = ::WaitForSingleObject(_lock, INFINITE);
    WINTER_ASSERT(error == WAIT_OBJECT_0);
  }

  bool try_lock() {
    return (::WaitForSingleObject(_lock, 0) == WAIT_OBJECT_0);
  }

  bool timed_lock(DWORD milliseconds) {
    return (::WaitForSingleObject(_lock, milliseconds) == WAIT_OBJECT_0);
  }

  void unlock() {
    BOOL error = ::ReleaseMutex(_lock);
    WINTER_ASSERT(error);
  }

private:
  HANDLE _lock;
};

class slim_rw_host : public services::noncopyable {
public:
  slim_rw_host() {
    ::InitializeSRWLock(&_lock);
  }

  ~slim_rw_host() {
  }

  void lock() {
    ::AcquireSRWLockExclusive(&_lock);
  }

  void unlock() {
    ::ReleaseSRWLockExclusive(&_lock);
  }

  void lock_shared() {
    ::AcquireSRWLockShared(&_lock);
  }

  void unlock_shared() {
    ::ReleaseSRWLockShared(&_lock);
  }

  void unlock_and_lock_shared() {
    unlock();
    lock_shared();
  }

private:
  SRWLOCK _lock;
};

}
}

#endif // SYNCHRONIZATION_HOST_H
